
#include "grid_common.h"

long stripColors[STRIP_LENGTH]; //Array with the color of each LED
long stripStates[] = {0,0,0,0,0}; //Array with color of each segment (CIRCLE, BOTTOM, LEFT...)
long colorsArray[] = {RED, GREEN, BLUE, CYAN, YELLOW, ORANGE, MAGENTA, WHITE, BLANK};

String TILE_NAMES[] = {"Self", "Bottom", "Left", "Top", "Right"};

int CKI = 42; //Clock and Data pins for LED
int SDI = 43;

int button = 53; //Simulator button

boolean readSwitch() {
  return digitalRead(button);
}

String getTileName(int idx) {
  return TILE_NAMES[idx];
}

void initCommon() {
  pinMode(button,INPUT);
  pinMode(SDI, OUTPUT);
  pinMode(CKI, OUTPUT);
}

static Coord origin;
static Coord myCoord; // Set these with Message Type 6
int getOriginX() {
  return origin.x;
}
void setOriginX(int newX) {
  origin.x = newX;
}
int getOriginY() {
  return origin.y;
}
void setOriginY(int newY) {
  origin.y = newY;
}
int getX() {
  return myCoord.x;
}
void setX(int newX) {
  myCoord.x = newX;
}
int getY() {
  return myCoord.y;
}
void setY(int newY) {
  myCoord.y = newY;
}

long getStripColors(int idx) {
  return stripColors[idx];
}

void setStripColors(int idx, long color) {
  stripColors[idx] = color;
}

long getStripStates(int idx) {
  return stripStates[idx];
}

void setStripStates(int idx, long newState) {
  stripStates[idx] = newState;
}

//NOTE: Speed is 1-10 where 1 is slowest and 10 is fastest. 5 is the reccomended speed
void pulseColor (long color2, int spd, int location) { //Nice visual effect commonly used to "blend" between colors
  long color1;
  if(location == -1){
    color1 = getStripStates(0);
  }else{
    color1 = getStripStates(location);
  }
  long strt[] = {(color1>>16) & 255, (color1>>8) & 255, color1 & 255};
  long stp[] = {(color2>>16) & 255, (color2>>8) & 255, color2 & 255};
  long dif[] = {strt[0]-stp[0], strt[1]-stp[1], strt[2]-stp[2]};

  
  int ldif = max( max(abs(dif[0]), abs(dif[1])), max(abs(dif[1]), abs(dif[2])));
  for(int i = 0; i<ldif/spd; i++){
      for(int i = 0; i<3; i++){
        if(strt[i]!= stp[i]){
          strt[i] = strt[i] - (((strt[i]-stp[i])/abs((strt[i]-stp[i])))*spd);
        }
      }  
      
    //Serial.println(String(strt[0])+ " " + String(strt[1]) + " " + String(strt[2]));
    long mycolor = strt[0]<<16 | strt[1]<<8 | strt[2];
    //Serial.println(mycolor);
    if(location == -1){
      for(int i =0; i<5; i++){
        turnOn(i, mycolor);  
      } 
    }else{
      turnOn(location, mycolor);
    }
    pushPixels();
    delayMicroseconds(5000);
  }
  if(location == -1){
    turnOnAll(color2);
  }else{
    turnOn(location, color2);
  }
  pushPixels();

}

//****************
// LED CODE
//****************

long currentColor; //Variable used by Interactive Game Mode (Move this?)

long getCurrentColor() {
  return currentColor;
}

void turnOn (byte Direction, long color) { //Turns on a specific segment. For a nicer effect, use pulseColor()
  currentColor = color;
  stripStates[Direction] = color;
  if(STRIP_LENGTH != 5){
    turnOnTile(Direction, color);
    return;  
  }
  switch (Direction){
    case CIRCLE:
      stripColors[0] = color;
      break;
    case BOTTOM:
      stripColors[1] = color;
      break;
    case LEFT:
      stripColors[4] = color;
      break;
    case TOP:
      stripColors[3] = color;
      break;
    case RIGHT:
      stripColors[2] = color;
      break;
  }
}

void turnOnTile (byte Direction, long color){ //"Redirect" method for real tiles called by turnOn()"

  switch (Direction){
    case CIRCLE:
      turnOn2(0,20, color);
      stripColors[37] = color;
      stripColors[23] = color;
      stripColors[44] = color;
      stripColors[30] = color;
      break;
    case BOTTOM:
      turnOn2(20,27, color);
      break;
    case LEFT:
      turnOn2(41,48, color);
      break;
    case TOP:
      turnOn2(27, 34, color);
      break;
    case RIGHT:
      turnOn2(34,41, color);
      break;
  }
}

void turnOnAll(long color){ //Turns on all the segments to a color
  currentColor = color;
  for(int i =0; i<5; i++){
    turnOn(i,color);  
  }
}

void turnOn2(int start, int stop, long color){ //Helper method for turnOnTile() that turns on the LEDs in a specific range
  for(int i = start; i<stop; i++){
    stripColors[i] = color;
  }  
}

void flashLight(int idx, long color) { //Helper method for Test Communication Mode
  for(int i = 0; i < 4; i++) {
    clearAll();
    turnOn(idx, i % 2 == 0 ? color : BLANK);
    pushPixels();
    delay(400);
  }
}

void pushPixel ( long Color ) { 
  for(byte color_bit = 23 ; color_bit != 255 ; color_bit--) {
    digitalWrite(CKI, LOW); //Only change data when clock is low
    long mask = 1L << color_bit;
    if(Color & mask) 
      digitalWrite(SDI, HIGH);
    else
      digitalWrite(SDI, LOW);
    digitalWrite(CKI, HIGH); //Data is latched when clock goes high
  }
}

void pushPixels (){ //Call this to update LED states
  for(int LED_number = 0; LED_number < STRIP_LENGTH; LED_number++)
    pushPixel(stripColors[LED_number]);
  digitalWrite(CKI, LOW);
  delayMicroseconds(500); //Wait for 500us to go into reset
}

void clearAll () {//Turn LEDs to blank (Call pushPixels() afterwards!)
  for (int i = 0; i < NUMBER_OF_PORTS; i++) {
    turnOn(i, BLANK);
    setColor(i, BLANK);
  }
}

//Next two methods are used for encoding and decoding colors for transmission over Serial
long numberToColor(byte number){ //Decodes number in to color
  if(number>=1 && number<=9) {
    return colorsArray[number-1];
  } else {
    return BLANK;
  }
  
}

int colorToNumber(long color){//Changes color to number to send of Serial
  for(int i = 0; i<9; i++){
    if(colorsArray[i] == color){
      return i+1;
    }
  }
  return 9;
}

void printMessage(int portIdx, int* values, int len) {
  if (DEBUG) {
    if (len > 0) {
      Serial.println();
      Serial.print("MESSAGE TYPE:  "); Serial.print(values[0]);
      Serial.print(" from "); Serial.println(TILE_NAMES[portIdx]);
      Serial.print("****Values: ");
      for (int i = 0; i < len; i++) {
        Serial.print(values[i]);
        if (i < len -1) {
          Serial.print(",");
        }
      }
      Serial.println("****\n");
      Serial.flush();
    }
  }
}
